Fun with an RTC and LCD

Published in Electronics

For ages I've had a Freetronics DS3232 module sitting around, for my latest afternoon spent fiddling with electronics I tested it out with an Arduino and a typical HD44780-compatible 16x2 LCD module. Thanks to some good libraries making a clock based around the DS3232 is really easy.

The DS3232 is fairly expensive for a clock chip but for the money is extremely easy to use and feature rich. It has calendar calculation on board, exposes its internal thermometer data and can even store some user data in its battery-backed RAM (just like good ol' Gameboy cartridges). I used Rhys Weatherley's DS3232 Arduino library to pull data from the RTC chip with good results - it did exactly what I wanted.

Pushing information on to the LCD was likewise extremely easy, thanks to libraries that someone had written. The LCD library that comes bundled with Arduino SDK - LiquidCrystal is easy to use.

The setup

This was a quick experiment to test out some parts, so all the connections were just breadboarded. Instead of using the recommended 10kΩ variable resistor pot to control the display contrast, I used a fixed 1.2kΩ resistor which worked with this particular screen, though it would likely be terrible with other models of HD44780 display. There was also a 10Ω resistor in line with the backlight to tone it down slightly. All other connections were straight through from the Arduino to the display and the RTC.

The Arduino sketch for this project was pretty simple - it mostly involved just moving data around once per second. If you have the same parts and want a quick way to test them out, the sketch source code can be downloaded here.

#include <SoftI2C.h>
#include <DS3232RTC.h>
#include <LiquidCrystal.h>

const byte LCD_WIDTH = 16, LCD_HEIGHT = 2, LCD_ROW_OFFSET = 0;
const String DAYS[] = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
const byte DAY_LENGTH = 3;
const byte ASCII_ZERO = 48;

SoftI2C i2c(A4, A5); //Set up I2C on analog 4 & 5 (SCA on 4, SCL on 5)
DS3232RTC rtcChip(i2c); //Initialise easy DS3232 RTC library
LiquidCrystal lcd(2, 3, 4, 5, 6, 7);

/* 
  The LCD circuit: - from Jeremy Blum's tutorial - https://www.youtube.com/watch?v=oIiDseJO4dM
 * LCD RS pin to digital pin 2
 * LCD Enable pin to digital pin 3
 * LCD D4 pin to digital pin 4
 * LCD D5 pin to digital pin 5
 * LCD D6 pin to digital pin 6
 * LCD D7 pin to digital pin 7
 * LCD R/W pin to ground
 * 10K resistor:
 * ends to +5V and ground
 * wiper to LCD VO pin (pin 3) OR use 4.7K fixed resistor from P3 to G for typical LCD
 */

byte lcdCol = 0, lcdRow = 0, charId = 0;

void setup() {
  //The only thing to do here is to initialise the LCD
  lcd.begin(LCD_WIDTH, LCD_HEIGHT);

}

void loop() {

  RTCDate date;
  RTCTime time;
  int temp = rtcChip.readTemperature();
  temp /= 4;

  rtcChip.readDate(&date);
  rtcChip.readTime(&time);

  lcdHome();

  lcdCharOut((time.hour   / 10) + ASCII_ZERO);
  lcdCharOut((time.hour   % 10) + ASCII_ZERO);
  lcdCharOut(':');
  lcdCharOut((time.minute / 10) + ASCII_ZERO);
  lcdCharOut((time.minute % 10) + ASCII_ZERO);
  lcdCharOut(':');
  lcdCharOut((time.second / 10) + ASCII_ZERO);
  lcdCharOut((time.second % 10) + ASCII_ZERO);

  lcdCol = LCD_WIDTH - 4;
  if(temp < 0)
    lcdCharOut('-');
  else
    lcdCharOut(' ');

  lcdCharOut((temp / 10) + ASCII_ZERO);
  lcdCharOut((temp % 10) + ASCII_ZERO);
  lcdCharOut(223); //Degree sign

  lcdRow = 1;

  lcdCharOut((date.day    / 10) + ASCII_ZERO);
  lcdCharOut((date.day    % 10) + ASCII_ZERO);
  lcdCharOut('-');
  lcdCharOut((date.month  / 10) + ASCII_ZERO);
  lcdCharOut((date.month  % 10) + ASCII_ZERO);
  lcdCharOut('-');
  lcdCharOut((date.year   / 1000) + ASCII_ZERO);
  lcdCharOut(((date.year   /  100) % 10) + ASCII_ZERO);
  lcdCharOut(((date.year   /   10) % 10) + ASCII_ZERO);
  lcdCharOut(((date.year         ) % 10) + ASCII_ZERO);

  lcdCol = LCD_WIDTH - DAY_LENGTH;
  lcd.setCursor(lcdCol, lcdRow + LCD_ROW_OFFSET);
  lcd.print(DAYS[calcDayOfWeek(date)]);

  //Align beginning of next loop iteration with the start of the next second (CPU time)
  int sleepTime = 1000 - (millis() % 1000);
  delay(sleepTime);
}

void lcdHome()
{
  lcdCol = 0;
  lcdRow = 0;
}

void lcdCharOut(char charcode)
{
   lcd.setCursor(lcdCol, lcdRow + LCD_ROW_OFFSET);
   lcd.write(charcode);
   //Advance cursor one column, wrap on same line if needed
   lcdCol = (lcdCol + 1) % LCD_WIDTH;
}

//Returns the index for the current day of the week (Sunday = 0)
byte calcDayOfWeek(RTCDate date) 
{
   //http://stackoverflow.com/questions/6054016/c-program-to-find-day-of-week-given-date
   long day = date.day, month = date.month, year = date.year;
   long val = day + ((153 * (month + 12 * ((14 - month) / 12) - 3) + 2) / 5) + (365 * (year + 4800 - ((14 - month) / 12))) + ((year + 4800 - ((14 - month) / 12)) / 4) - ((year + 4800 - ((14 - month) / 12)) / 100) + ((year + 4800 - ((14 - month) / 12)) / 400) - 32045;
   return (val + 1) % 7;
}

So what next?

This was just a quick experiment to test out the LCD and RTC, but with the capabilities of these chips it's possible to make a timekeeping device that would make high parts cost worthwhile. The same manufacturer also offers the DS3231 that does almost the same thing (just without any RAM available to the user), which would be fine for the sketch above.